home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / ArchiFacile / ArchiFacileSetup.exe / {app} / nw.pak / Unnamed File 000145.txt < prev    next >
Text File  |  2014-10-14  |  19KB  |  692 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. cr.define('cr.ui', function() {
  6.   // require cr.ui.define
  7.   // require cr.ui.limitInputWidth
  8.  
  9.   /**
  10.    * The number of pixels to indent per level.
  11.    * @type {number}
  12.    * @const
  13.    */
  14.   var INDENT = 20;
  15.  
  16.   /**
  17.    * Returns the computed style for an element.
  18.    * @param {!Element} el The element to get the computed style for.
  19.    * @return {!CSSStyleDeclaration} The computed style.
  20.    */
  21.   function getComputedStyle(el) {
  22.     return el.ownerDocument.defaultView.getComputedStyle(el);
  23.   }
  24.  
  25.   /**
  26.    * Helper function that finds the first ancestor tree item.
  27.    * @param {!Element} el The element to start searching from.
  28.    * @return {cr.ui.TreeItem} The found tree item or null if not found.
  29.    */
  30.   function findTreeItem(el) {
  31.     while (el && !(el instanceof TreeItem)) {
  32.       el = el.parentNode;
  33.     }
  34.     return el;
  35.   }
  36.  
  37.   /**
  38.    * Creates a new tree element.
  39.    * @param {Object=} opt_propertyBag Optional properties.
  40.    * @constructor
  41.    * @extends {HTMLElement}
  42.    */
  43.   var Tree = cr.ui.define('tree');
  44.  
  45.   Tree.prototype = {
  46.     __proto__: HTMLElement.prototype,
  47.  
  48.     /**
  49.      * Initializes the element.
  50.      */
  51.     decorate: function() {
  52.       // Make list focusable
  53.       if (!this.hasAttribute('tabindex'))
  54.         this.tabIndex = 0;
  55.  
  56.       this.addEventListener('click', this.handleClick);
  57.       this.addEventListener('mousedown', this.handleMouseDown);
  58.       this.addEventListener('dblclick', this.handleDblClick);
  59.       this.addEventListener('keydown', this.handleKeyDown);
  60.     },
  61.  
  62.     /**
  63.      * Returns the tree item that are children of this tree.
  64.      */
  65.     get items() {
  66.       return this.children;
  67.     },
  68.  
  69.     /**
  70.      * Adds a tree item to the tree.
  71.      * @param {!cr.ui.TreeItem} treeItem The item to add.
  72.      */
  73.     add: function(treeItem) {
  74.       this.addAt(treeItem, 0xffffffff);
  75.     },
  76.  
  77.     /**
  78.      * Adds a tree item at the given index.
  79.      * @param {!cr.ui.TreeItem} treeItem The item to add.
  80.      * @param {number} index The index where we want to add the item.
  81.      */
  82.     addAt: function(treeItem, index) {
  83.       this.insertBefore(treeItem, this.children[index]);
  84.       treeItem.setDepth_(this.depth + 1);
  85.     },
  86.  
  87.     /**
  88.      * Removes a tree item child.
  89.      * @param {!cr.ui.TreeItem} treeItem The tree item to remove.
  90.      */
  91.     remove: function(treeItem) {
  92.       this.removeChild(treeItem);
  93.     },
  94.  
  95.     /**
  96.      * The depth of the node. This is 0 for the tree itself.
  97.      * @type {number}
  98.      */
  99.     get depth() {
  100.       return 0;
  101.     },
  102.  
  103.     /**
  104.      * Handles click events on the tree and forwards the event to the relevant
  105.      * tree items as necesary.
  106.      * @param {Event} e The click event object.
  107.      */
  108.     handleClick: function(e) {
  109.       var treeItem = findTreeItem(e.target);
  110.       if (treeItem)
  111.         treeItem.handleClick(e);
  112.     },
  113.  
  114.     handleMouseDown: function(e) {
  115.       if (e.button == 2) // right
  116.         this.handleClick(e);
  117.     },
  118.  
  119.     /**
  120.      * Handles double click events on the tree.
  121.      * @param {Event} e The dblclick event object.
  122.      */
  123.     handleDblClick: function(e) {
  124.       var treeItem = findTreeItem(e.target);
  125.       if (treeItem)
  126.         treeItem.expanded = !treeItem.expanded;
  127.     },
  128.  
  129.     /**
  130.      * Handles keydown events on the tree and updates selection and exanding
  131.      * of tree items.
  132.      * @param {Event} e The click event object.
  133.      */
  134.     handleKeyDown: function(e) {
  135.       var itemToSelect;
  136.       if (e.ctrlKey)
  137.         return;
  138.  
  139.       var item = this.selectedItem;
  140.       if (!item)
  141.         return;
  142.  
  143.       var rtl = getComputedStyle(item).direction == 'rtl';
  144.  
  145.       switch (e.keyIdentifier) {
  146.         case 'Up':
  147.           itemToSelect = item ? getPrevious(item) :
  148.               this.items[this.items.length - 1];
  149.           break;
  150.         case 'Down':
  151.           itemToSelect = item ? getNext(item) :
  152.               this.items[0];
  153.           break;
  154.         case 'Left':
  155.         case 'Right':
  156.           // Don't let back/forward keyboard shortcuts be used.
  157.           if (!cr.isMac && e.altKey || cr.isMac && e.metaKey)
  158.             break;
  159.  
  160.           if (e.keyIdentifier == 'Left' && !rtl ||
  161.               e.keyIdentifier == 'Right' && rtl) {
  162.             if (item.expanded)
  163.               item.expanded = false;
  164.             else
  165.               itemToSelect = findTreeItem(item.parentNode);
  166.           } else {
  167.             if (!item.expanded)
  168.               item.expanded = true;
  169.             else
  170.               itemToSelect = item.items[0];
  171.           }
  172.           break;
  173.         case 'Home':
  174.           itemToSelect = this.items[0];
  175.           break;
  176.         case 'End':
  177.           itemToSelect = this.items[this.items.length - 1];
  178.           break;
  179.       }
  180.  
  181.       if (itemToSelect) {
  182.         itemToSelect.selected = true;
  183.         e.preventDefault();
  184.       }
  185.     },
  186.  
  187.     /**
  188.      * The selected tree item or null if none.
  189.      * @type {cr.ui.TreeItem}
  190.      */
  191.     get selectedItem() {
  192.       return this.selectedItem_ || null;
  193.     },
  194.     set selectedItem(item) {
  195.       var oldSelectedItem = this.selectedItem_;
  196.       if (oldSelectedItem != item) {
  197.         // Set the selectedItem_ before deselecting the old item since we only
  198.         // want one change when moving between items.
  199.         this.selectedItem_ = item;
  200.  
  201.         if (oldSelectedItem)
  202.           oldSelectedItem.selected = false;
  203.  
  204.         if (item) {
  205.           item.selected = true;
  206.           if (item.id)
  207.             this.setAttribute('aria-activedescendant', item.id);
  208.         } else {
  209.             this.removeAttribute('aria-activedescendant');
  210.         }
  211.         cr.dispatchSimpleEvent(this, 'change');
  212.       }
  213.     },
  214.  
  215.     /**
  216.      * @return {!ClientRect} The rect to use for the context menu.
  217.      */
  218.     getRectForContextMenu: function() {
  219.       // TODO(arv): Add trait support so we can share more code between trees
  220.       // and lists.
  221.       if (this.selectedItem)
  222.         return this.selectedItem.rowElement.getBoundingClientRect();
  223.       return this.getBoundingClientRect();
  224.     }
  225.   };
  226.  
  227.   /**
  228.    * Determines the visibility of icons next to the treeItem labels. If set to
  229.    * 'hidden', no space is reserved for icons and no icons are displayed next
  230.    * to treeItem labels. If set to 'parent', folder icons will be displayed
  231.    * next to expandable parent nodes. If set to 'all' folder icons will be
  232.    * displayed next to all nodes. Icons can be set using the treeItem's icon
  233.    * property.
  234.    */
  235.   cr.defineProperty(Tree, 'iconVisibility', cr.PropertyKind.ATTR);
  236.  
  237.   /**
  238.    * Incremental counter for an auto generated ID of the tree item. This will
  239.    * be incremented per element, so each element never share same ID.
  240.    *
  241.    * @type {number}
  242.    */
  243.   var treeItemAutoGeneratedIdCounter = 0;
  244.  
  245.   /**
  246.    * This is used as a blueprint for new tree item elements.
  247.    * @type {!HTMLElement}
  248.    */
  249.   var treeItemProto = (function() {
  250.     var treeItem = cr.doc.createElement('div');
  251.     treeItem.className = 'tree-item';
  252.     treeItem.innerHTML = '<div class=tree-row>' +
  253.         '<span class=expand-icon></span>' +
  254.         '<span class=tree-label></span>' +
  255.         '</div>' +
  256.         '<div class=tree-children></div>';
  257.     treeItem.setAttribute('role', 'treeitem');
  258.     treeItem.id = 'tree-item-autogen-id-' + treeItemAutoGeneratedIdCounter++;
  259.     return treeItem;
  260.   })();
  261.  
  262.   /**
  263.    * Creates a new tree item.
  264.    * @param {Object=} opt_propertyBag Optional properties.
  265.    * @constructor
  266.    * @extends {HTMLElement}
  267.    */
  268.   var TreeItem = cr.ui.define(function() {
  269.     return treeItemProto.cloneNode(true);
  270.   });
  271.  
  272.   TreeItem.prototype = {
  273.     __proto__: HTMLElement.prototype,
  274.  
  275.     /**
  276.      * Initializes the element.
  277.      */
  278.     decorate: function() {
  279.  
  280.     },
  281.  
  282.     /**
  283.      * The tree items children.
  284.      */
  285.     get items() {
  286.       return this.lastElementChild.children;
  287.     },
  288.  
  289.     /**
  290.      * The depth of the tree item.
  291.      * @type {number}
  292.      */
  293.     depth_: 0,
  294.     get depth() {
  295.       return this.depth_;
  296.     },
  297.  
  298.     /**
  299.      * Sets the depth.
  300.      * @param {number} depth The new depth.
  301.      * @private
  302.      */
  303.     setDepth_: function(depth) {
  304.       if (depth != this.depth_) {
  305.         this.rowElement.style.WebkitPaddingStart = Math.max(0, depth - 1) *
  306.             INDENT + 'px';
  307.         this.depth_ = depth;
  308.         var items = this.items;
  309.         for (var i = 0, item; item = items[i]; i++) {
  310.           item.setDepth_(depth + 1);
  311.         }
  312.       }
  313.     },
  314.  
  315.     /**
  316.      * Adds a tree item as a child.
  317.      * @param {!cr.ui.TreeItem} child The child to add.
  318.      */
  319.     add: function(child) {
  320.       this.addAt(child, 0xffffffff);
  321.     },
  322.  
  323.     /**
  324.      * Adds a tree item as a child at a given index.
  325.      * @param {!cr.ui.TreeItem} child The child to add.
  326.      * @param {number} index The index where to add the child.
  327.      */
  328.     addAt: function(child, index) {
  329.       this.lastElementChild.insertBefore(child, this.items[index]);
  330.       if (this.items.length == 1)
  331.         this.hasChildren = true;
  332.       child.setDepth_(this.depth + 1);
  333.     },
  334.  
  335.     /**
  336.      * Removes a child.
  337.      * @param {!cr.ui.TreeItem} child The tree item child to remove.
  338.      */
  339.     remove: function(child) {
  340.       // If we removed the selected item we should become selected.
  341.       var tree = this.tree;
  342.       var selectedItem = tree.selectedItem;
  343.       if (selectedItem && child.contains(selectedItem))
  344.         this.selected = true;
  345.  
  346.       this.lastElementChild.removeChild(child);
  347.       if (this.items.length == 0)
  348.         this.hasChildren = false;
  349.     },
  350.  
  351.     /**
  352.      * The parent tree item.
  353.      * @type {!cr.ui.Tree|cr.ui.TreeItem}
  354.      */
  355.     get parentItem() {
  356.       var p = this.parentNode;
  357.       while (p && !(p instanceof TreeItem) && !(p instanceof Tree)) {
  358.         p = p.parentNode;
  359.       }
  360.       return p;
  361.     },
  362.  
  363.     /**
  364.      * The tree that the tree item belongs to or null of no added to a tree.
  365.      * @type {cr.ui.Tree}
  366.      */
  367.     get tree() {
  368.       var t = this.parentItem;
  369.       while (t && !(t instanceof Tree)) {
  370.         t = t.parentItem;
  371.       }
  372.       return t;
  373.     },
  374.  
  375.     /**
  376.      * Whether the tree item is expanded or not.
  377.      * @type {boolean}
  378.      */
  379.     get expanded() {
  380.       return this.hasAttribute('expanded');
  381.     },
  382.     set expanded(b) {
  383.       if (this.expanded == b)
  384.         return;
  385.  
  386.       var treeChildren = this.lastElementChild;
  387.  
  388.       if (b) {
  389.         if (this.mayHaveChildren_) {
  390.           this.setAttribute('expanded', '');
  391.           treeChildren.setAttribute('expanded', '');
  392.           cr.dispatchSimpleEvent(this, 'expand', true);
  393.           this.scrollIntoViewIfNeeded(false);
  394.         }
  395.       } else {
  396.         var tree = this.tree;
  397.         if (tree && !this.selected) {
  398.           var oldSelected = tree.selectedItem;
  399.           if (oldSelected && this.contains(oldSelected))
  400.             this.selected = true;
  401.         }
  402.         this.removeAttribute('expanded');
  403.         treeChildren.removeAttribute('expanded');
  404.         cr.dispatchSimpleEvent(this, 'collapse', true);
  405.       }
  406.     },
  407.  
  408.     /**
  409.      * Expands all parent items.
  410.      */
  411.     reveal: function() {
  412.       var pi = this.parentItem;
  413.       while (pi && !(pi instanceof Tree)) {
  414.         pi.expanded = true;
  415.         pi = pi.parentItem;
  416.       }
  417.     },
  418.  
  419.     /**
  420.      * The element representing the row that gets highlighted.
  421.      * @type {!HTMLElement}
  422.      */
  423.     get rowElement() {
  424.       return this.firstElementChild;
  425.     },
  426.  
  427.     /**
  428.      * The element containing the label text and the icon.
  429.      * @type {!HTMLElement}
  430.      */
  431.     get labelElement() {
  432.       return this.firstElementChild.lastElementChild;
  433.     },
  434.  
  435.     /**
  436.      * The label text.
  437.      * @type {string}
  438.      */
  439.     get label() {
  440.       return this.labelElement.textContent;
  441.     },
  442.     set label(s) {
  443.       this.labelElement.textContent = s;
  444.     },
  445.  
  446.     /**
  447.      * The URL for the icon.
  448.      * @type {string}
  449.      */
  450.     get icon() {
  451.       return getComputedStyle(this.labelElement).backgroundImage.slice(4, -1);
  452.     },
  453.     set icon(icon) {
  454.       return this.labelElement.style.backgroundImage = url(icon);
  455.     },
  456.  
  457.     /**
  458.      * Whether the tree item is selected or not.
  459.      * @type {boolean}
  460.      */
  461.     get selected() {
  462.       return this.hasAttribute('selected');
  463.     },
  464.     set selected(b) {
  465.       if (this.selected == b)
  466.         return;
  467.       var rowItem = this.firstElementChild;
  468.       var tree = this.tree;
  469.       if (b) {
  470.         this.setAttribute('selected', '');
  471.         rowItem.setAttribute('selected', '');
  472.         this.reveal();
  473.         this.labelElement.scrollIntoViewIfNeeded(false);
  474.         if (tree)
  475.           tree.selectedItem = this;
  476.       } else {
  477.         this.removeAttribute('selected');
  478.         rowItem.removeAttribute('selected');
  479.         if (tree && tree.selectedItem == this)
  480.           tree.selectedItem = null;
  481.       }
  482.     },
  483.  
  484.     /**
  485.      * Whether the tree item has children.
  486.      * @type {boolean}
  487.      */
  488.     get mayHaveChildren_() {
  489.       return this.hasAttribute('may-have-children');
  490.     },
  491.     set mayHaveChildren_(b) {
  492.       var rowItem = this.firstElementChild;
  493.       if (b) {
  494.         this.setAttribute('may-have-children', '');
  495.         rowItem.setAttribute('may-have-children', '');
  496.       } else {
  497.         this.removeAttribute('may-have-children');
  498.         rowItem.removeAttribute('may-have-children');
  499.       }
  500.     },
  501.  
  502.     /**
  503.      * Whether the tree item has children.
  504.      * @type {boolean}
  505.      */
  506.     get hasChildren() {
  507.       return !!this.items[0];
  508.     },
  509.  
  510.     /**
  511.      * Whether the tree item has children.
  512.      * @type {boolean}
  513.      */
  514.     set hasChildren(b) {
  515.       var rowItem = this.firstElementChild;
  516.       this.setAttribute('has-children', b);
  517.       rowItem.setAttribute('has-children', b);
  518.       if (b)
  519.         this.mayHaveChildren_ = true;
  520.     },
  521.  
  522.     /**
  523.      * Called when the user clicks on a tree item. This is forwarded from the
  524.      * cr.ui.Tree.
  525.      * @param {Event} e The click event.
  526.      */
  527.     handleClick: function(e) {
  528.       if (e.target.className == 'expand-icon')
  529.         this.expanded = !this.expanded;
  530.       else
  531.         this.selected = true;
  532.     },
  533.  
  534.     /**
  535.      * Makes the tree item user editable. If the user renamed the item a
  536.      * bubbling {@code rename} event is fired.
  537.      * @type {boolean}
  538.      */
  539.     set editing(editing) {
  540.       var oldEditing = this.editing;
  541.       if (editing == oldEditing)
  542.         return;
  543.  
  544.       var self = this;
  545.       var labelEl = this.labelElement;
  546.       var text = this.label;
  547.       var input;
  548.  
  549.       // Handles enter and escape which trigger reset and commit respectively.
  550.       function handleKeydown(e) {
  551.         // Make sure that the tree does not handle the key.
  552.         e.stopPropagation();
  553.  
  554.         // Calling tree.focus blurs the input which will make the tree item
  555.         // non editable.
  556.         switch (e.keyIdentifier) {
  557.           case 'U+001B':  // Esc
  558.             input.value = text;
  559.             // fall through
  560.           case 'Enter':
  561.             self.tree.focus();
  562.         }
  563.       }
  564.  
  565.       function stopPropagation(e) {
  566.         e.stopPropagation();
  567.       }
  568.  
  569.       if (editing) {
  570.         this.selected = true;
  571.         this.setAttribute('editing', '');
  572.         this.draggable = false;
  573.  
  574.         // We create an input[type=text] and copy over the label value. When
  575.         // the input loses focus we set editing to false again.
  576.         input = this.ownerDocument.createElement('input');
  577.         input.value = text;
  578.         if (labelEl.firstChild)
  579.           labelEl.replaceChild(input, labelEl.firstChild);
  580.         else
  581.           labelEl.appendChild(input);
  582.  
  583.         input.addEventListener('keydown', handleKeydown);
  584.         input.addEventListener('blur', (function() {
  585.           this.editing = false;
  586.         }).bind(this));
  587.  
  588.         // Make sure that double clicks do not expand and collapse the tree
  589.         // item.
  590.         var eventsToStop = ['mousedown', 'mouseup', 'contextmenu', 'dblclick'];
  591.         eventsToStop.forEach(function(type) {
  592.           input.addEventListener(type, stopPropagation);
  593.         });
  594.  
  595.         // Wait for the input element to recieve focus before sizing it.
  596.         var rowElement = this.rowElement;
  597.         function onFocus() {
  598.           input.removeEventListener('focus', onFocus);
  599.           // 20 = the padding and border of the tree-row
  600.           cr.ui.limitInputWidth(input, rowElement, 100);
  601.         }
  602.         input.addEventListener('focus', onFocus);
  603.         input.focus();
  604.         input.select();
  605.  
  606.         this.oldLabel_ = text;
  607.       } else {
  608.         this.removeAttribute('editing');
  609.         this.draggable = true;
  610.         input = labelEl.firstChild;
  611.         var value = input.value;
  612.         if (/^\s*$/.test(value)) {
  613.           labelEl.textContent = this.oldLabel_;
  614.         } else {
  615.           labelEl.textContent = value;
  616.           if (value != this.oldLabel_) {
  617.             cr.dispatchSimpleEvent(this, 'rename', true);
  618.           }
  619.         }
  620.         delete this.oldLabel_;
  621.       }
  622.     },
  623.  
  624.     get editing() {
  625.       return this.hasAttribute('editing');
  626.     }
  627.   };
  628.  
  629.   /**
  630.    * Helper function that returns the next visible tree item.
  631.    * @param {cr.ui.TreeItem} item The tree item.
  632.    * @return {cr.ui.TreeItem} The found item or null.
  633.    */
  634.   function getNext(item) {
  635.     if (item.expanded) {
  636.       var firstChild = item.items[0];
  637.       if (firstChild) {
  638.         return firstChild;
  639.       }
  640.     }
  641.  
  642.     return getNextHelper(item);
  643.   }
  644.  
  645.   /**
  646.    * Another helper function that returns the next visible tree item.
  647.    * @param {cr.ui.TreeItem} item The tree item.
  648.    * @return {cr.ui.TreeItem} The found item or null.
  649.    */
  650.   function getNextHelper(item) {
  651.     if (!item)
  652.       return null;
  653.  
  654.     var nextSibling = item.nextElementSibling;
  655.     if (nextSibling) {
  656.       return nextSibling;
  657.     }
  658.     return getNextHelper(item.parentItem);
  659.   }
  660.  
  661.   /**
  662.    * Helper function that returns the previous visible tree item.
  663.    * @param {cr.ui.TreeItem} item The tree item.
  664.    * @return {cr.ui.TreeItem} The found item or null.
  665.    */
  666.   function getPrevious(item) {
  667.     var previousSibling = item.previousElementSibling;
  668.     return previousSibling ? getLastHelper(previousSibling) : item.parentItem;
  669.   }
  670.  
  671.   /**
  672.    * Helper function that returns the last visible tree item in the subtree.
  673.    * @param {cr.ui.TreeItem} item The item to find the last visible item for.
  674.    * @return {cr.ui.TreeItem} The found item or null.
  675.    */
  676.   function getLastHelper(item) {
  677.     if (!item)
  678.       return null;
  679.     if (item.expanded && item.hasChildren) {
  680.       var lastChild = item.items[item.items.length - 1];
  681.       return getLastHelper(lastChild);
  682.     }
  683.     return item;
  684.   }
  685.  
  686.   // Export
  687.   return {
  688.     Tree: Tree,
  689.     TreeItem: TreeItem
  690.   };
  691. });
  692.